﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security;
using System.Xml;
using System.Net;
using Google.GData.Calendar;
using Google.GData.Client;
using Google.GData.Extensions;

namespace GoogleAppsTools
{
    class GoogleCalendarChecker
    {
        /// <summary>
        /// Creates the authentication object
        /// </summary>
        public static CalendarService GetService(string applicationName, string userName, string password)
        {
            CalendarService service = new CalendarService(applicationName);
            service.setUserCredentials(userName, password);
            return service;
        }

        /// <summary>
        /// Returns a list containing all the events in all the calendars in an account.
        /// </summary>
        public static List<CalendarInfo> CheckForCalendarEvents(string username, string password)
        {
            List<CalendarInfo> AllCalendars = new List<CalendarInfo>(); //the list that is returned. Each object is a list of events and a string of the calendar name.
            List<Calendar> Calendars = new List<Calendar>(); //the list that holds the names and links to all the calendars

            try
            {
                Service service = GoogleCalendarChecker.GetService("cl", username, password);//new Service("cl", "companyName-appName-1"); //the service object to get the list of calendars
                FeedQuery query = new FeedQuery();

                query.Uri = new Uri("https://www.google.com/calendar/feeds/" + username + "/allcalendars/full"); //the link to get a list of all calendars
                AtomFeed calendarsFeed = service.Query(query); //run the query

                foreach (AtomEntry entry in calendarsFeed.Entries) //for each entry returned in the query add the name and link to the calendars list
                {
                    Calendars.Add(new Calendar(entry.Title.Text.ToString(), entry.Links[0].HRef.ToString()));
                }

                foreach (Calendar calendar in Calendars) //for each calendar found run a search within it for events that happen today
                {
                    CalendarInfo genericEvents = new CalendarInfo() { Title = calendar.Title }; //the list that will hold all the events
                    //create the date objects based on the settings kept in Properties.Settings
                    string beginningOfDay;
                    string endOfDay = DateTime.Now.ToString("yyyy-MM-ddT") + "23:59:59";

                    if (Properties.Settings.Default.ShowPastEvents)
                        beginningOfDay = DateTime.Now.ToString("yyyy-MM-ddT") + "00:00:00"; //retrieve all events for today
                    else
                        beginningOfDay = DateTime.Now.ToString("yyyy-MM-ddTH:mm:ss"); //or only the ones left to go

                    EventQuery calendarQuery = new EventQuery(); //create a new query object to run a seperate query for each calendar found (this is a different sort of query object to the one that finds the calendars) 
                    CalendarService individualService = new CalendarService("aaa"); //create a new service object too
                    individualService.setUserCredentials(username, password); //set the user credentials
                    calendarQuery.Uri = new Uri(calendar.Link);//set the link
                    calendarQuery.ExtraParameters = "start-min=" + beginningOfDay + "&start-max=" + endOfDay; //set the start/end date parameters
                    EventFeed calendarFeed = individualService.Query(calendarQuery); //run the search

                    foreach (EventEntry googleEvent in calendarFeed.Entries) //for each event that is found create a genericEvent object and add it to the list to be returned
                    {
                        GenericEvent genericEvent = new GenericEvent();
                        genericEvent.Title = googleEvent.Title.Text;
                        if (genericEvent.Title == "")
                            genericEvent.Title = "(no title)";
                        genericEvent.Contents = googleEvent.Content.Content;
                        genericEvent.Location = googleEvent.Locations.First().ValueString;
                        genericEvent.StartTime = googleEvent.Times.First().StartTime;
                        genericEvent.EndTime = googleEvent.Times.First().EndTime;
                        genericEvent.Link = googleEvent.Links[0].HRef.ToString();
                        genericEvents.CalendarItems.Add(genericEvent);

                        //System.Windows.Forms.MessageBox.Show(genericEvent.Title  + "\n" + genericEvent.Contents  + "\n" + genericEvent.Location  + "\n" + genericEvent.StartTime.ToString() + "\n" + genericEvent.EndTime.ToString()  + "\n" + genericEvent.Link);
                    }
                    AllCalendars.Add(genericEvents);

                }
            }
            catch (Exception)
            {
                //let the main application catch the error
            }
            
            return AllCalendars; //return the list
        }

        public static bool checkIfNewBitmapRequired(List<CalendarInfo> newEvents, List<CalendarInfo> oldEvents, bool thumbnailsCreated)
        {
            if (newEvents == null || oldEvents == null)
                return true;

            if (thumbnailsCreated == false)
                return true;

            foreach (CalendarInfo newCalendar in newEvents)
            {
                foreach (CalendarInfo oldCalendar in oldEvents)
                {
                    
                if (newCalendar.Title == oldCalendar.Title && newCalendar.CalendarItems.Count < oldCalendar.CalendarItems.Count)
                    return true;
                if (newCalendar.Title == oldCalendar.Title && newCalendar.CalendarItems.Count > oldCalendar.CalendarItems.Count)
                    return true;

                if (newCalendar.Title == oldCalendar.Title && newCalendar.CalendarItems.Count == oldCalendar.CalendarItems.Count)
                {
                    foreach (GenericEvent newItem in newCalendar.CalendarItems)
                    {
                        int count = 0;
                        foreach (GenericEvent oldItem in oldCalendar.CalendarItems)
                        {
                         if (newItem == oldItem)
                            {
                                //System.Windows.Forms.MessageBox.Show(newMailItem.Title + " = " + oldMailItem.Title);
                                count++;
                            }
                        }
                        if (count == 0)
                            return true;
                    }
                }
                }
            }
            return false;
        }
    }

    /// <summary>
    /// Connects to the google servers and calls the AddEvent method to add a new event to the calendar
    /// </summary>
    public class GoogleEventsSyncDataSource
    {
        CalendarService service;

        public GoogleEventsSyncDataSource()
        {
            service = GoogleCalendarChecker.GetService("MyCalendar", EncryptionClass.ToInsecureString(EncryptionClass.DecryptString(Properties.Settings.Default.username)), EncryptionClass.ToInsecureString(EncryptionClass.DecryptString(Properties.Settings.Default.password)));
        }

        #region ISyncDataSource<GenericEvent> Members

        public string Id
        {
            get { return "Google Calander Sync Data Source"; }
        }

        public void WriteItem(GenericEvent item)
        {
            CalendarHelper.AddEvent(service, item.Title, item.Contents, item.Location, item.StartTime, item.EndTime, item.AllDay);
        }

        #endregion
    }

    /// <summary>
    /// Worker class that actually adds a new event to the calendar
    /// </summary>
    class CalendarHelper
    {
        public static void AddEvent(CalendarService service, string title, string contents, string location, DateTime startTime, DateTime endTime, bool allDay)
        {
            EventEntry entry = new EventEntry();
            try
            {
                // Set the title and content of the entry.
                entry.Title.Text = title;
                entry.Content.Content = contents;

                // Set a location for the event.
                Where eventLocation = new Where();
                eventLocation.ValueString = location;
                entry.Locations.Add(eventLocation);

                When eventTime = new When(startTime, endTime, allDay);
                entry.Times.Add(eventTime);

                Uri postUri = new Uri
                ("http://www.google.com/calendar/feeds/default/private/full");

                // Send the request and receive the response:
                AtomEntry insertedEntry = service.Insert(postUri, entry);
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show("Unable to add new event: \n \n" + ex.ToString());
            }
        } 
    }

    /// <summary>
    /// Class to define a calendar object - name of calendar and link to it
    /// </summary>
    class Calendar
    {
        private string title;
        private string link;

        public string Title
        {
            get { return title; }
        }

        public string Link
        {
            get { return link; }
        }

        public Calendar(string title, string link)
        {
            this.title = title;
            this.link = link;
        }

    } 

    /// <summary>
    /// Class to hold all details of a calendar - it's name and a list of calendar events
    /// </summary>
    class CalendarInfo
    {
        private string title;
        public List<GenericEvent> CalendarItems = new List<GenericEvent>();

        public string Title
        {
            get { return title; }
            set { title = value; }
        }
    }

    /// <summary>
    /// Class to define a single event
    /// </summary>
    public class GenericEvent : IEquatable<GenericEvent>
    {
        public string Title { get; set; }
        public string Contents { get; set; }
        public string Location { get; set; }
        public DateTime StartTime { get; set; }
        public DateTime EndTime { get; set; }
        public bool AllDay { get; set; }
        public string Link { get; set; }

        #region IEquatable<GenericEvent> Members

        public bool Equals(GenericEvent other)
        {
            //Compare all fields to check equality
            if (this.Title == other.Title &&
                this.Contents == other.Contents &&
                this.Location == other.Location &&
                this.StartTime == other.StartTime &&
                this.EndTime == other.EndTime)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        #endregion

        /// <summary>
        /// This works out when the event is starting/finishing, and tailors the string output accordingly
        /// </summary>
        /// <returns></returns>
        public string[] ToStringArray(bool includeCommas)
        {
            string[] returnValue = new string[2];
                            returnValue[0] = this.Title;

            if (this.StartTime.ToShortTimeString() == "00:00" && this.EndTime.ToShortTimeString() == "00:00" || this.StartTime.ToShortDateString() != DateTime.Now.ToShortDateString() && this.EndTime.ToShortDateString() != DateTime.Now.ToShortDateString()) //if all day event, start date not today and end date not today then no times
            {
                returnValue[1] = "";
                return returnValue; 
            }
            else if (this.StartTime.ToShortDateString() == DateTime.Now.ToShortDateString() && this.StartTime.ToShortTimeString() != "00:00" && this.EndTime.ToShortTimeString() != "00:00" && this.EndTime.ToShortDateString() != DateTime.Now.ToShortDateString()) //if event starts today, is not all day and end time not today just show start time
            {
                if(includeCommas)
                    returnValue[0] += ", ";
                returnValue[1] = "starts " + this.StartTime.ToShortTimeString();
                return returnValue; 
            }
            else if (this.StartTime.ToShortDateString() != DateTime.Now.ToShortDateString() && this.StartTime.ToShortTimeString() != "00:00" && this.EndTime.ToShortTimeString() != "00:00" && this.EndTime.ToShortDateString() == DateTime.Now.ToShortDateString()) //if event does not start today, is not an all day event but finishes today just show end time
            {
                if (includeCommas)
                    returnValue[0] += ", ";
                returnValue[1] = "finishes " + this.EndTime.ToShortTimeString();
                return returnValue;
            }
            else
            {
                if (includeCommas)
                    returnValue[0] += ", ";
                returnValue[1] = this.StartTime.ToShortTimeString() + "-" + this.EndTime.ToShortTimeString(); //for all other events show start and end time (no date)
                return returnValue;
            }
        }
    }
}
